﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NeuralStock.DomainService.Interfaces;
using NeuralStock.DataAccess;

namespace NeuralStock.DomainService.Implementation
{
    public class CompanyQuotationService : BaseService, ICompanyQuotationService
    {
        public bool QuotationExists(int companyId, DateTime date)
        {
            return DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .Where(x => x.Date == date)
                .Any();
        }

        public bool QuotationsExist(int companyId, int yearFrom, int yearTo)
        {
            return DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId && x.Date.Year == yearFrom && x.Date.Month == 1 && x.Date.Day < 8).Any()
                && DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId && x.Date.Year == yearTo && x.Date.Month == 12 && x.Date.Day > 23).Any();
        }

        public void CreateQuotation(int companyId, DateTime date, decimal price)
        {
            CompanyQuotation quot = new CompanyQuotation();
            quot.CompanyId = companyId;
            quot.Date = date;
            quot.Price = price;

            DbContext.CompanyQuotation.AddObject(quot);

            DbContext.SaveChanges();
        }

        public void UpdateQuotation(int companyId, DateTime date, decimal price)
        {
            CompanyQuotation quot = DbContext.CompanyQuotation
                .Single(x => x.CompanyId == companyId
                && x.Date == date);

            quot.Price = price;

            DbContext.SaveChanges();
        }

        #region Get

        public CompanyQuotation Get(int companyId, DateTime date)
        {
            return DbContext.CompanyQuotation
                .FirstOrDefault(x => x.CompanyId == companyId
                    && x.Date == date);
        }

        public IEnumerable<CompanyQuotation> GetQuotationsForYears(int companyId, int yearFrom, int yearTo)
        {
            return DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .Where(x => x.Date.Year >= yearFrom)
                .Where(x => x.Date.Year <= yearTo);
        }

        public decimal GetMaxReturnForCompany(int companyId, int yearFrom, int yearTo)
        {
            var quotes = GetQuotationsForYears(companyId, yearFrom, yearTo).OrderBy(x => x.Date).ToList();

            var firstQuotePrice = quotes.First().Price;
            var maxQuotePrice = quotes.Max(x => x.Price);

            return (maxQuotePrice - firstQuotePrice) / firstQuotePrice;
        }

        public decimal GetMaxReturnForCompanyStartingAfterFirstQuarter(int companyId, int yearFrom, int yearTo)
        {
            var quotes = GetQuotesStartingAfterFirstQuarter(companyId, yearFrom, yearTo);

            var firstQuote = GetQuoteAfterFirstQuarter(companyId, yearFrom);
            var maxQuotePrice = quotes.Max(x => x.Price);

            decimal maxReturn = (maxQuotePrice - firstQuote.Price) / firstQuote.Price;

            return maxReturn;
        }

        public decimal GetReturnAfterWholePeriodStartingAfterFirstQuarter(int companyId, int yearFrom, int yearTo)
        {
            var quotes = GetQuotesStartingAfterFirstQuarter(companyId, yearFrom, yearTo);

            var firstQuotePrice = GetQuoteAfterFirstQuarter(companyId, yearFrom).Price;
            var lastQuotePrice = quotes.Last().Price;

            return (lastQuotePrice - firstQuotePrice) / firstQuotePrice;
        }

        public int? GetDaysForAchievingReturnStartingAfterFirstQuarter(int companyId, int yearFrom, int yearTo, decimal expectedReturn)
        {
            var quotes = GetQuotesStartingAfterFirstQuarter(companyId, yearFrom, yearTo);
            var firstQuote = GetQuoteAfterFirstQuarter(companyId, yearFrom);

            decimal expectedPrice = firstQuote.Price * (1 + expectedReturn);

            var sellQuote = quotes.FirstOrDefault(x => x.Price >= expectedPrice);

            if (sellQuote == null)
            {
                return null;
            }

            return (sellQuote.Date - firstQuote.Date).Days;
        }

        public List<CompanyQuotation> GetQuotesStartingAfterFirstQuarter(int companyId, int yearFrom, int yearTo)
        {
            var quarterYearQuotes = DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .Where(x => x.Date.Year == yearFrom)
                .Where(x => x.Date.Month >= 5);

            var fullYearQuotes = DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .Where(x => x.Date.Year >= yearFrom + 1)
                .Where(x => x.Date.Year <= yearTo);

            var quotes = quarterYearQuotes.Union(fullYearQuotes).OrderBy(x => x.Date).ToList();

            return quotes;
        }

        public CompanyQuotation GetQuoteAfterFirstQuarter(int companyId, int year)
        {
            return DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .Where(x => x.Date.Year == year)
                .Where(x => x.Date.Month == 4)
                .OrderBy(x => x.Date)
                .ToList()
                .Last();
        }

        public decimal? GetMinPriceForYear(int companyId, int year)
        {
            var quotes = DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .Where(x => x.Date.Year == year);

            if (!quotes.Any())
            {
                return null;
            }

            return quotes.Min(x => x.Price);
        }

        public decimal? GetAvgPriceForYear(int companyId, int year)
        {
            var quotes = DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .Where(x => x.Date.Year == year);

            if (!quotes.Any())
            {
                return null;
            }

            return quotes.Select(x => x.Price).Average();
        }

        public decimal GetLastPrice(int companyId)
        {
            var lastQuotation = DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .OrderByDescending(x => x.Date)
                .First();

            if ((DateTime.Today - lastQuotation.Date).TotalDays > 30)
            {
                throw new InvalidOperationException(string.Format("Quotations for CompanyId: {0} are extremely not current", companyId));
            }

            return lastQuotation.Price;
        }

        public DateTime GetFirstQuotationDate(int companyId)
        {
            var firstQuotation = DbContext.CompanyQuotation
                .Where(x => x.CompanyId == companyId)
                .OrderBy(x => x.Date)
                .First();

            return firstQuotation.Date;
        }

        #endregion
    }
}
